﻿#region License
/* Copyright (C) 2009 Tim Coulter
 * 
 * This file is part of ExtremeML.
 * 
 * ExtremeML is free software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public
 * License, as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/.
*/
#endregion

using System;
using System.Text;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using ExtremeML.Spreadsheet.RichText;

namespace ExtremeML.Spreadsheet
{
	// This is a partial class that adds custom functionality to its auto-generated partner.
	public sealed partial class RunWrapper
	{
		#region Custom Properties


		#endregion
		#region Custom Methods


		#endregion
		#region Overrides


		#endregion
		#region Implementation Methods


		#endregion
		#region Static Methods

		/// <summary>
		/// Creates a set of <see cref="RunWrapper"/> instances that contains the parsed value of the passed rich text string.
		/// </summary>
		/// <param name="richText">A rich text string that conforms to the supported markup scheme.</param>
		/// <returns>An <see cref="IEnumerable{RunWrapper}"/> that contains the parsed result.</returns>
		internal static IEnumerable<Run> Parse(
			string richText)
		{
			if (string.IsNullOrEmpty(richText)) throw new ArgumentNullException("richText");

			const string pattern = @"(?<open><[\w\s=""#]+>)|(?<close></[\w]+>)|(?<content>[^<]+)";

			var result = new List<Run>();
			var fonts = new Stack<FontDefinition>();
			var alignments = new Stack<AlignmentDefinition>();
			var properties = RunPropertiesWrapper.CreateDefaultElement();

			fonts.Push(new FontDefinition(properties));
			alignments.Push(new AlignmentDefinition(properties));

			foreach (Match match in Regex.Matches(richText, pattern))
			{
				if (match.Groups["content"].Success)
				{
					var text = TextWrapper.CreateDefaultElement();
					text.Text = match.Groups["content"].Value;
					result.Add(new Run((RunProperties) properties.Clone(), text));
				}
				else if (match.Groups["open"].Success)
				{
					switch (Regex.Match(match.Groups["open"].Value, @"<(\w+)").Groups[1].Value.ToLower())
					{
						case "b": // bold
							properties.RemoveAllChildren<Bold>();
							properties.AppendChild(new Bold());
							break;
						case "u": // underline
							properties.RemoveAllChildren<Underline>();
							properties.AppendChild(new Underline());
							break;
						case "i": // italic
							properties.RemoveAllChildren<Italic>();
							properties.AppendChild(new Italic());
							break;
						case "strike": // strike
							properties.RemoveAllChildren<Strike>();
							properties.AppendChild(new Strike());
							break;
						case "outline": // outline
							properties.RemoveAllChildren<Outline>();
							properties.AppendChild(new Outline());
							break;
						case "shadow": // shadow
							properties.RemoveAllChildren<Shadow>();
							properties.AppendChild(new Shadow());
							break;
						case "condense": // condense
							properties.RemoveAllChildren<Condense>();
							properties.AppendChild(new Condense());
							break;
						case "extend": // extend
							properties.RemoveAllChildren<Extend>();
							properties.AppendChild(new Extend());
							break;
						case "font": // font
							var font = new FontDefinition(fonts.Peek(), match.Groups["open"].Value);
							fonts.Push(font);
							font.Populate(properties);
							break;
						case "valign": // valign
							var align = new AlignmentDefinition(match.Groups["open"].Value);
							alignments.Push(align);
							align.Populate(properties);
							break;
						default:
							break;
					}
				}
				else if (match.Groups["close"].Success)
				{
					switch (Regex.Match(match.Groups["close"].Value, @"</([A-Za-z]+)").Groups[1].Value.ToLower())
					{
						case "b": // bold
							properties.RemoveAllChildren<Bold>();
							break;
						case "u": // underline
							properties.RemoveAllChildren<Underline>();
							break;
						case "i": // italic
							properties.RemoveAllChildren<Italic>();
							break;
						case "strike": // strike
							properties.RemoveAllChildren<Strike>();
							break;
						case "outline": // outline
							properties.RemoveAllChildren<Outline>();
							break;
						case "shadow": // shadow
							properties.RemoveAllChildren<Shadow>();
							break;
						case "condense": // condense
							properties.RemoveAllChildren<Condense>();
							break;
						case "extend": // extend
							properties.RemoveAllChildren<Extend>();
							break;
						case "font": // font
							fonts.Pop();
							fonts.Peek().Populate(properties);
							break;
						case "valign": // valign
							alignments.Pop();
							alignments.Peek().Populate(properties);
							break;
						default:
							break;
					}
				}
			}

			return result;
		}

		/// <summary>
		/// Combines the passed set of <see cref="RunWrapper"/> instances into a rich text string.
		/// </summary>
		/// <param name="values">An <see cref="IEnumerable{RunWrapper}"/> that contains the values to combine.</param>
		/// <returns>A rich text string that conforms to the supported markup scheme.</returns>
		internal static string Combine(
			IEnumerable<Run> values)
		{
			var builder = new StringBuilder();

			foreach (var value in values)
			{
//ToDo: this is incomplete. It merely concatenates the text components without the formating tags
				builder.Append(value.Text.Text);				
			}

			return builder.ToString();
		}

		#endregion
	}
}
